
<!DOCTYPE html>
<html>
  <head>
    
<meta charset="utf-8" >

<title>C++ 学习心得(更新中） | Blog</title>
<meta name="description" content="个人生活，学习记录">

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.7.0/animate.min.css">

<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
<link rel="shortcut icon" href="https://qiaoxu123.github.io/favicon.ico?v=1589072669553">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.10.0/katex.min.css">
<link rel="stylesheet" href="https://qiaoxu123.github.io/styles/main.css">



<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.bootcss.com/highlight.js/9.12.0/highlight.min.js"></script>



  </head>
  <body>
    <div id="app" class="main">
      <div class="site-header-container">
  <div class="site-header">
    <div class="left">
      <a href="https://qiaoxu123.github.io">
        <img class="avatar" src="https://qiaoxu123.github.io/images/avatar.png?v=1589072669553" alt="" width="32px" height="32px">
      </a>
      <a href="https://qiaoxu123.github.io">
        <h1 class="site-title">Blog</h1>
      </a>
    </div>
    <div class="right">
      <transition name="fade">
        <i class="icon" :class="{ 'icon-close-outline': menuVisible, 'icon-menu-outline': !menuVisible }" @click="menuVisible = !menuVisible"></i>
      </transition>
    </div>
  </div>
</div>

<transition name="fade">
  <div class="menu-container" style="display: none;" v-show="menuVisible">
    <div class="menu-list">
      
        
          <a href="https://qiaoxu123.github.io" class="menu purple-link">
            首页
          </a>
        
      
        
          <a href="https://qiaoxu123.github.io/archives" class="menu purple-link">
            归档
          </a>
        
      
        
          <a href="https://qiaoxu123.github.io/tags" class="menu purple-link">
            标签
          </a>
        
      
    </div>
  </div>
</transition>


      <div class="content-container">
        <div class="post-detail">
          
          <h2 class="post-title">C++ 学习心得(更新中）</h2>
          <div class="post-info post-detail-info">
            <span><i class="icon-calendar-outline"></i> 2020-05-01</span>
            
              <span>
                <i class="icon-pricetags-outline"></i>
                
                  <a href="https://qiaoxu123.github.io/tag/KUvEXNYC1/">
                    个人学习
                    
                  </a>
                
              </span>
            
          </div>
          <div class="post-content">
            <p>最近一直在看《C++ Primer》，实际接触这本圣经后真是爱恨有加，要是能早点静下心来阅读的话，想必现在水平也不至于此吧。好在现在还有机会，准备一口气读完。有点意外地是，虽然囫囵吞枣阅读，但在实际工程应用上，有很多技巧已经可见一斑了。过程中结合《Effective C++》的阅读，有了更深的体会。所以在这想陆续更新一些心得，一方面是作为自己的学习笔记，另一方面也算是督促自己反复思考，不能停止。路还很长，希望再接再厉吧~</p>
<h2 id="-more-"><!-- more --></h2>
<p>效仿瑞·达利欧的《原则》，也整理几条自己觉得超有用的C++使用原则，下面正式开始。</p>
<h2 id="1-尽可能使用const">1. 尽可能使用const</h2>
<blockquote>
<p>本条也是《Effective C++》第三版中条款3，具体可以参考原书。<br>
补充参考<a href="https://light-city.club/sc/basic_content/const/">链接</a>，一个个人感觉很好的C++学习资料</p>
</blockquote>
<p>const作为限定符，在C++编程时用的非常多，基本已经上升到一种编程习惯的程度了，在我理解看来。也就是说，在定义类型，声明函数以及形参，返回值时，都尽可能定义为const。对我而言，习惯使用const意味着会时刻考虑到该变量是否需要改变，在其生存周期中。对于函数形参以及函数的返回值也是如此，下面举例说明下。</p>
<pre><code class="language-c++">#include &lt;iostream&gt;

class Test {
public:
    void getSum() const { return sum; }
    void add(const int&amp; a, const int&amp; b) { sum = a + b; }
private:
    int sum;
};

int main() {
    Test obj;
    obj.add(1, 2);
    std::cout &lt;&lt; obj.getSum() &lt;&lt; std::endl;
    return 0;
}
</code></pre>
<p>示例中以类成员函数举例，<code>getSum()</code>函数作为一个内联函数，作用是返回<code>sum</code>成员变量。由于对成员变量本身没有改变，所以可以在函数末尾添加const声明。其实，<code>getSum()</code>函数本身已经隐式传入了该类的<code>this</code>指针，返回值相当于<code>this-&gt;sum</code>。这样可以更容易理解。对于第二个函数，传入参数也不会改变，因此对其形参声明为const。</p>
<p>除了以上例子之外，const的默认应用还有很多，比如for循环中使用迭代器遍历vector容器时，比较好的做法是使用cbegin()替代原来的begin()，在确定迭代过程中不发发生改变时。</p>
<hr>
<h2 id="2-尽可能使用引用">2. 尽可能使用引用</h2>
<p>引用也是C++标志性的特性之一，和const一样，强烈推荐在可以使用的场合尽可能使用引用。仍然使用上面的例子，在函数形参传参时，如果不使用引用传参的话，那么函数形参需要进行拷贝，也就是常说的&quot;pass by value&quot;。这个过程对于调用函数来说代价很大，特别是形参是类对象时，还需要考虑到类的初始化等操作。而使用引用，即&quot;pass by reference&quot;来实现，由于其底层仍然是调用的指针，因此只需要访问地址即可，极大地提高了效率。</p>
<p>需要注意一点，如果希望引用作为形参而不希望在函数中被改变的话，可以将其声明为const，确保过程的安全。具体可以参考《Effective C++》中条款20。</p>
<hr>
<h2 id="3-初始化类成员变量时时尽可能使用列表初始化">3. 初始化类成员变量时时，尽可能使用列表初始化</h2>
<blockquote>
<p>本条与《Effective C++》中条款4相对应，具体请看原书</p>
</blockquote>
<p>我们都知道在书写类时需要构造函数来初始化对象，但由于C++复杂的特性，对于该实现过程往往注意不到，也因此容易导致一些意外问题，这是题外话了。当我们在初始化C++中成员变量时，需要注意，不要混淆了赋值(assignment)和初始化(initialization)操作。下面举个简单的例子说明下。</p>
<pre><code class="language-c++">#include &lt;iostream&gt;
#include &lt;string&gt;

class add {
public:
  // add() = default;
  //  add(const int&amp; a, const int&amp; b) {  
  //      x = a;   // 这些都是赋值，而非初始化
  //      y = b;
  //  }
  add(const int&amp; a, const int&amp; b) : a(a), b(b) { }
  void getSum() const { return a + b; } 
   ~add() = default;
private:
    int a;
    int b;
    // int a = 0;   // 赋值初始化
    // int b = 0;
    std::string s;
}

int main() {
    add obj(1, 2);
    obj.getSum()
}
</code></pre>
<p>上面例子中的三个构造函数，分别说明下。(引用Primer中的介绍)</p>
<ul>
<li>
<p>第一个构造函数，首先明确一点，因为该构造函数不接受任何实参，所以它是一个默认构造函数。我们定义这个构造函数的目的仅仅是因为我们既需要其他形式的构造函数，也需要默认的构造函数。我们希望这个函数的作用完全等同于之前使用的合成默认构造函数。<br>
在C++11新标准中，如果我们需要默认的行为，那么可以通过在参数列表后面写上<code>= default</code>来要求编译器生成构造函数。具体请看Primer中7.1节介绍。这是一种初始化方法。<strong>注意，这个默认构造函数之所以有效，是因为我们为内置类型的数据成员提供了初始值。</strong></p>
</li>
<li>
<p>第二个构造函数，也是很多初学者的习惯用法。但需要明白，对象的成员变量初始化动作发生在进入构造函数本体之前，因此，变量<code>x, y</code> 并不是被初始化，而是被赋值。</p>
</li>
<li>
<p>第三个构造函数，也是强烈推荐的实现方式。使用所谓的 member initialization list (成员初值列)替换赋值动作。这个构造函数和上一个结果相同，但效率较高。基于赋值的那个版本首先调用default构造函数，然后立刻再对它们赋予新值。default构造函数的一切作为因此浪费了。</p>
</li>
</ul>
<p>需要注意的是，即使面对的成员变量属于内置类型(那么其初始化与赋值的成本相同), 也一定得使用初值列。如果成员变量是const或references，它们就一定需要初值，不能被赋值。见《Effective C++》条款5。</p>
<hr>
<h2 id="4-尽可能使用c标准的强制类型转换">4. 尽可能使用C++标准的强制类型转换</h2>
<p>C++中提供了四种显式的强制类型转换，<code>static_cast</code>，<code>dynamic_cast</code>, <code>const_cast</code>，<code>reinterpret_cast</code>，其中最常用的是<code>static_cast</code>和<code>const_cast</code>。为什么推荐尽可能使用它们呢，是因为隐式的类型转换默认的，也因此出现的问题我们没有那么容易发现。通过使用显式地转换，我们能够意识到这种转换可能导致的隐患，从而规避它。</p>
<p>两个的应用场景很容易区分，当底层不是const构成，转换为另一种类型时，使用<code>static_cast</code>。而希望将const类型转换为非const，使用<code>const_cast</code>，从而将常量对象转换为非常量对象。</p>
<hr>
<h2 id="5-标准的遍历容器的for循环的写法">5. 标准的遍历容器的for循环的写法</h2>
<p>在这以vector数组举例，解释下我认为比较标准的两种遍历方式。</p>
<pre><code class="language-c++">vector&lt;int&gt; arr {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

// 1. 使用范围for语句

for (const auto &amp; i : arr) {
    std::cout &lt;&lt; i &lt;&lt; std::endl;
}

// 2. 经典循环

for (size_t i = 0; i != arr.size(); ++ i) {
    std::cout &lt;&lt; i &lt;&lt; std::endl;
}

// 3. 迭代器遍历

for (auto iter = arr.cbegin(); iter != arr.cend(); ++ iter) {
    std::cout &lt;&lt; *iter &lt;&lt; std::endl;
}

</code></pre>
<p>这三种方式应该都很熟悉，不再赘述。稍微提一下第二种，循环变量<code>i</code>定义为<code>size_t</code>，是因为vector容易<code>size()</code>函数即<code>size_t</code>类型，这种写法比之前定义<code>int</code>类型要好。</p>
<hr>
<h2 id="6-根据实际情况灵活使用auto">6. 根据实际情况，灵活使用auto</h2>
<p>在前面几个条款中，已经多次使用过auto关键字了。一般是用在明确知道其类型，但因为类型太长不想写的时候。。。当然，范围for循环是例外</p>

          </div>
        </div>

        
          <div class="next-post">
            <a class="purple-link" href="https://qiaoxu123.github.io/post/ubuntu-change-fonts/">
              <h3 class="post-title">
                下一篇：将 Linux 上的 VSCode 改为 Consolas 字体
              </h3>
            </a>
          </div>
          
      </div>

      

      <div class="site-footer">
  <div class="slogan">个人生活，学习记录</div>
  <div class="social-container">
    
      
    
      
    
      
    
      
    
      
    
  </div>
  Powered by <a href="https://github.com/getgridea/gridea" target="_blank">Gridea</a> | <a class="rss" href="https://qiaoxu123.github.io/atom.xml" target="_blank">RSS</a>
</div>


    </div>
    <script type="application/javascript">

hljs.initHighlightingOnLoad()

var app = new Vue({
  el: '#app',
  data: {
    menuVisible: false,
  },
})

</script>




  </body>
</html>
